﻿using System;
using System.Windows.Markup;

namespace ICodeShare.UI.Converters.Markup
{
    #region MarkupExtension
    /// <summary>
    /// 类型转换器，在设定的两种类型间进行转换
    /// </summary>
    /// <example>
    /// 1.在xmal文件引用DateConverter类所在命名空间。
    ///    xmlns:cvts="http://schemas.extended.wpf.com/converters"
    /// 2.在xaml文件中指定Binding值的Converter
    ///    <TextBox Text="{Binding Age, Converter={cvts:ObjectTypeConverter SourceType=sys:Int32, TargetType=sys:String}}"/>
    ///    <TextBox Text="{Binding text, Converter={cvts:ObjectTypeConverter TargetType="{x:Type sys:DateTime}"}}"/>
    /// </example>
    [MarkupExtensionReturnType(typeof(ObjectTypeConverter))]
    public sealed class ObjectTypeConverterExtension : MarkupExtension
    {
        private readonly TypeExtension sourceTypeExtension;
        private readonly TypeExtension targetTypeExtension;

        /// <summary>
        /// Initializes a new instance of the ObjectTypeConverterExtension class.
        /// </summary>
        public ObjectTypeConverterExtension()
        {
            this.sourceTypeExtension = new TypeExtension();
            this.targetTypeExtension = new TypeExtension();
        }

        /// <summary>
        /// Initializes a new instance of the ObjectTypeConverterExtension class with the specified source and target types.
        /// </summary>
        /// <param name="sourceType">
        /// The source type for the <see cref="ObjectTypeConverter"/>.
        /// </param>
        /// <param name="targetType">
        /// The target type for the <see cref="ObjectTypeConverter"/>.
        /// </param>
        public ObjectTypeConverterExtension(Type sourceType, Type targetType)
        {
            this.sourceTypeExtension = new TypeExtension(sourceType);
            this.targetTypeExtension = new TypeExtension(targetType);
        }

        /// <summary>
        /// Initializes a new instance of the ObjectTypeConverterExtension class with the specified source and target types.
        /// </summary>
        /// <param name="sourceTypeName">
        /// The source type name for the <see cref="ObjectTypeConverter"/>.
        /// </param>
        /// <param name="targetTypeName">
        /// The target type name for the <see cref="ObjectTypeConverter"/>.
        /// </param>
        public ObjectTypeConverterExtension(string sourceTypeName, string targetTypeName)
        {
            this.sourceTypeExtension = new TypeExtension(sourceTypeName);
            this.targetTypeExtension = new TypeExtension(targetTypeName);
        }

        /// <summary>
        /// Gets or sets the source type for the <see cref="ObjectTypeConverter"/>.
        /// </summary>
        [ConstructorArgument("sourceType")]
        public Type SourceType
        {
            get { return this.sourceTypeExtension.Type; }
            set { this.sourceTypeExtension.Type = value; }
        }

        /// <summary>
        /// Gets or sets the target type for the <see cref="ObjectTypeConverter"/>.
        /// </summary>
        [ConstructorArgument("targetType")]
        public Type TargetType
        {
            get { return this.targetTypeExtension.Type; }
            set { this.targetTypeExtension.Type = value; }
        }

        /// <summary>
        /// Gets or sets the name of the source type for the <see cref="ObjectTypeConverter"/>.
        /// </summary>
        [ConstructorArgument("sourceTypeName")]
        public string SourceTypeName
        {
            get { return this.sourceTypeExtension.TypeName; }
            set { this.sourceTypeExtension.TypeName = value; }
        }

        /// <summary>
        /// Gets or sets the name of the target type for the <see cref="ObjectTypeConverter"/>.
        /// </summary>
        [ConstructorArgument("targetTypeName")]
        public string TargetTypeName
        {
            get { return this.targetTypeExtension.TypeName; }
            set { this.targetTypeExtension.TypeName = value; }
        }

        /// <summary>
        /// Provides an instance of <see cref="ObjectTypeConverter"/> based on this <c>ObjectTypeConverterExtension</c>.
        /// </summary>
        /// <param name="serviceProvider">
        /// An object that can provide services.
        /// </param>
        /// <returns>
        /// The instance of <see cref="ObjectTypeConverter"/>.
        /// </returns>
        public override object ProvideValue(IServiceProvider serviceProvider)
        {
            Type sourceType = null;
            Type targetType = null;

            if ((this.sourceTypeExtension.Type != null) || (this.sourceTypeExtension.TypeName != null))
            {
                sourceType = this.sourceTypeExtension.ProvideValue(serviceProvider) as Type;
            }

            if ((this.targetTypeExtension.Type != null) || (this.targetTypeExtension.TypeName != null))
            {
                targetType = this.targetTypeExtension.ProvideValue(serviceProvider) as Type;
            }

            // just let the TypeExtensions do the type resolving via the service provider
            return new ObjectTypeConverter(sourceType, targetType);
        }
    }
    #endregion
}
